﻿using System;
using System.Collections.Generic;
using Magic;
using System.IO;

namespace RoMFramework
{
    public class PointerManager
    {
        public readonly Dictionary<string, IntPtr> PointersDictionary = new Dictionary<string, IntPtr>();
        public readonly Dictionary<string, IntPtr> AddressesDictionary = new Dictionary<string, IntPtr>();
        public FindPattern PatternSearcher;
        private byte[] m_ProcessData, m_TemporaryByte;
        private IntPtr m_TemporaryPointer;

        private readonly BlackMagic m_BMagic;

        /// <summary>
        /// PointerManager Constructor
        /// </summary>
        /// <param name="bm">A <see cref="BlackMagic"/> that is used to dump the memory from.</param>
        public PointerManager(BlackMagic bm)
        {
            m_BMagic = bm;
            Console.Instance.AddLine("Initialised Pointer Scanner");
        }

        /// <summary>
        /// Dumps the process memory
        /// </summary>
        /// <param name="force">If true</param>
        private void DumpMemory(bool force = false)
        {
            if (CheckDumpedMemory() && !force)
                return;
            try
            {
                m_ProcessData = m_BMagic.ReadBytes((uint) m_BMagic.MainModule.BaseAddress,
                                                    m_BMagic.MainModule.ModuleMemorySize);
                if (!CheckDumpedMemory())
                {
                    throw new Exception("There was an error dumping the memory");
                }
                Console.Instance.AddLine("Memory Dumped Successfully");
            }

            catch (Exception e)
            {
                m_ProcessData = null;
                Console.Instance.AddLine(e.Message);
            }
        }

        /// <summary>
        /// Checks that the memory is dumped
        /// </summary>
        /// <returns></returns>
        private bool CheckDumpedMemory()
        {
            if (m_ProcessData != null)
            {
                if (m_ProcessData.Length == m_BMagic.MainModule.ModuleMemorySize)
                {
                    //Memory hopefully dumped properly, continuing on.
                    return true;
                }
            }
            //The Integrity check failed, returning false
            return false;
        }
        
        /// <summary>
        /// Finds the patterns listed in the file
        /// </summary>
        /// <param name="patternFile">The file to load the patterns from</param>
        public void SearchPatterns(string patternFile)
        {
            if (!CheckDumpedMemory())
            {
                DumpMemory();
            }
            if (!CheckDumpedMemory())
            {
                Console.Instance.AddLine("Memory Dump Failed");
                return;
            }
            try
            {
                PatternSearcher = new FindPattern(patternFile, (uint) m_BMagic.MainModule.BaseAddress, ref m_ProcessData);
                Console.Instance.AddLine("Patterns Found Successfully");
                ReadPointers();

            }
            catch (Exception ex)
            {
                Console.Instance.AddLine("There was an error finding the Patterns");
                Console.Instance.AddLine(ex.Message);
            }
        }

        /// <summary>
        /// Reads the pointers from the patterns found with usage = "Pointer" 
        /// </summary>
        private void ReadPointers()
        {
            try
            {
                foreach (var kvp in PatternSearcher.PointersDictionary)
                {
                    m_TemporaryByte = m_BMagic.ReadBytes(kvp.Value, 4);
                    m_TemporaryPointer = new IntPtr(BitConverter.ToUInt32(m_TemporaryByte, 0));
                    PointersDictionary.Add(kvp.Key, m_TemporaryPointer);
                }
                foreach (var kvp in PatternSearcher.AddressesDictionary)
                {
                    AddressesDictionary.Add(kvp.Key, new IntPtr(kvp.Value));
                }
                Console.Instance.AddLine("Pointers Read Successfully");
            }
            catch (Exception ex)
            {
                Console.Instance.AddLine("There was an error reading the pointers");
                Console.Instance.AddLine(ex.Message);
            }
        }

        /// <summary>
        /// Retrieves the specified pointer from Pointer Dictionary converted to uint
        /// </summary>
        /// <param name="pointer">The name of the pointer to get</param>
        /// <returns>The pointer converted to uint</returns>
        public uint PointerToUint(string pointer)
        {
            try
            {
                return (uint) PointersDictionary[pointer].ToInt32();
            }
            catch (Exception ex)
            {
                Console.Instance.AddLine(
                    "There was an error retrieving the pointer, make sure you specified the right name");
                Console.Instance.AddLine(ex.Message);
                return 0;
            }
        }
    }
}